﻿using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;
using Game;
using Mekiasm.Common;
using Mekiasm.ElectricElement;
using Mekiasm.FluidElement;
using static Mekiasm.MekBlock;

namespace Mekiasm
{
    public class MekWire : ComplexModelItem
    {
        public class DrawCache
        {
            public ModelItem item;
            public bool isVisible;            
        }

        public enum WireNameType
        {
            Base, Advance, Elite, Ultimate
        }

        public WireNameType NameType;
        public int GetOutInLimit(WireNameType wireNameType)
        {
            switch (wireNameType)
            {
                case WireNameType.Base: return 32;
                case WireNameType.Advance: return 128;
                case WireNameType.Elite: return 512;
                case WireNameType.Ultimate: return 2048;
                default: return 0;
            }
        }
        public DeviceType DeviceType;
        public ModelItem CoreItem;
        public MekWire(WireNameType nameType, DeviceType deviceType) : base(new ModelItem[] {
            new ModelItem("0", $"MekWire/{nameType}_{deviceType}"), new ModelItem("1", $"MekWire/{nameType}_{deviceType}"),
            new ModelItem("2", $"MekWire/{nameType}_{deviceType}"), new ModelItem("3", $"MekWire/{nameType}_{deviceType}"),
            new ModelItem("4", $"MekWire/{nameType}_{deviceType}"), new ModelItem("5", $"MekWire/{nameType}_{deviceType}"),
            new ModelItem("core", $"MekWire/{nameType}_{deviceType}") }, "MekWire",null)
        {
            DeviceType = deviceType;
            NameType = nameType;
            IconScale = 2f;
            IsAllowRotate = false;
            string pre = "", pre2 = "";
            switch (NameType)
            {
                case WireNameType.Base: pre = "基础"; break;
                case WireNameType.Advance: pre = "高级"; break;
                case WireNameType.Elite: pre = "精英"; break;
                case WireNameType.Ultimate: pre = "终极"; break;
                default: break;
            }
            switch (DeviceType)
            {
                case DeviceType.Electric: pre2 = "能量线缆"; break;
                case DeviceType.Fluid: pre2 = "流体管道"; break;
                case DeviceType.Gas: pre2 = "加压管道"; break;
                case DeviceType.Item: pre2 = "物流管道"; break;
                default: break;
            }
            DisplayName = pre + pre2;
            CoreItem = ModelItems.FirstOrDefault(p => p.meshName == "core");
        }
        public Dictionary<Point3, DrawCache[]> ElectricPoints = new Dictionary<Point3, DrawCache[]>();
        public Dictionary<Point3, DrawCache[]> FliudPoints = new Dictionary<Point3, DrawCache[]>();
        public Dictionary<Point3, DrawCache[]> GasPoints = new Dictionary<Point3, DrawCache[]>();
        public override void DrawBlock(PrimitivesRenderer3D primitivesRenderer, int value, Color color, float size, ref Matrix matrix, DrawBlockEnvironmentData environmentData)
        {
            BlocksManager.DrawMeshBlock(primitivesRenderer, CoreItem.BlockMesh, CoreItem.Texture2D, color, size * IconScale, ref matrix, environmentData);
        }
        public override bool OnUse(Ray3 aim, ComponentMiner componentMiner, TerrainRaycastResult? raycast)
        {
            return false;
        }
        public override bool OnInteract(SubsystemBlockBehavior blockBehavior, TerrainRaycastResult raycastResult, ComponentMiner componentMiner)
        {
            return false;
        }
        public override void GetDropValues(SubsystemTerrain subsystemTerrain, int oldValue, int newValue, int toolLevel, List<BlockDropValue> dropValues, out bool showDebris)
        {
            dropValues.Add(new BlockDropValue() { Value = ILibrary.GetBasicValue(oldValue), Count = 1 });
            showDebris = true;
        }
        public override bool IsInteractive(SubsystemTerrain subsystemTerrain, int value)
        {
            return false;
        }
        public override void GenerateTerrainVertices(Block block, BlockGeometryGenerator generator, TerrainGeometry geometry, int value, int x, int y, int z)
        {
            Point3 point = new Point3(x, y, z);
            DrawCache[] drawCaches = new DrawCache[7];
            //验证六个面是否可见
            for (int i = 0; i < 6; i++)
            {
                drawCaches[i] = new DrawCache();
                Point3 next = CellFace.FaceToPoint3(i) + point;
                ModelItem modelItem = ModelItems.FirstOrDefault(p => p.meshName.Contains(i.ToString()));
                drawCaches[i].item = modelItem;
                drawCaches[i].isVisible = false;
                ComponentBlockEntity blockEntity = ILibrary.SubsystemBlockEntities.GetBlockEntity(next.X, next.Y, next.Z);
                if (blockEntity != null)
                {
                    IInventory inventory = blockEntity.Entity.FindComponent<IInventory>();
                    if (inventory is ComponentEnergyMachine)
                    {
                        ComponentEnergyMachine energyMachine = inventory as ComponentEnergyMachine;
                        if (energyMachine.MachineFaceConfig.TryGetValue(CellFace.OppositeFace(i), out MachineFaceConfig machineFace))
                        {
                            if (machineFace.DeviceType == DeviceType)
                            {
                                drawCaches[i].isVisible = true;
                            }
                        }
                    }
                    else
                    {
                        if (DeviceType == DeviceType.Item)
                        {
                            drawCaches[i].isVisible = true;
                        }
                    }
                }
                else
                {
                    int v = ILibrary.SubsystemTerrain.Terrain.GetCellValueFast(next.X, next.Y, next.Z);
                    int Id = Terrain.ExtractContents(v);
                    if (MekiasmInit.MekBlocks.TryGetValue(Id, out MekBlock mekBlock))
                    {
                        if (mekBlock.GetDeviceType(v).HasFlag(DeviceType))
                        {
                            drawCaches[i].isVisible = true;

                        }
                    }
                }
            }
            drawCaches[6] = new DrawCache();
            drawCaches[6].isVisible = true;
            drawCaches[6].item = ModelItems.FirstOrDefault(m => m.meshName == "core");
            for (int i = 0; i < drawCaches.Length; i++)
            {
                if (drawCaches[i].isVisible)
                {
                    var model = drawCaches[i].item;
                    generator.GenerateMeshVertices(block, x, y, z, model.BlockMesh, Color.White, null, geometry.GetGeometry(model.Texture2D).SubsetAlphaTest);
                }
            }
            switch (DeviceType)
            {
                case DeviceType.Electric:
                    if (!ElectricPoints.ContainsKey(point)) ElectricPoints.Add(point, drawCaches);
                    break;
                case DeviceType.Fluid:
                    if (!FliudPoints.ContainsKey(point)) FliudPoints.Add(point, drawCaches);
                    break;
                case DeviceType.Gas:
                    if (!GasPoints.ContainsKey(point)) GasPoints.Add(point, drawCaches);
                    break;
            }
        }

        public override BoundingBox[] GetCustomCollisionBoxes(SubsystemTerrain terrain, MovingBlock value)
        {
            List<BoundingBox> boundingBoxes = new List<BoundingBox>();
            DrawCache[] drawCaches = null;
            switch (DeviceType)
            {
                case DeviceType.Electric:
                    ElectricPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
                case DeviceType.Fluid:
                    FliudPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
                case DeviceType.Gas:
                    GasPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
            }
            if (drawCaches == null) return Block.m_defaultCollisionBoxes;
            for (int i = 0; i < drawCaches.Length; i++)
            {
                if (drawCaches[i].isVisible) boundingBoxes.Add(drawCaches[i].item.BlockMesh.CalculateBoundingBox());
            }
            if (boundingBoxes.Count == 0)
            {
                boundingBoxes.Add(new BoundingBox(Vector3.Zero, Vector3.One));
            }
            return boundingBoxes.ToArray();
        }
        public override BoundingBox[] GetCustomInteractionBoxes(SubsystemTerrain terrain, MovingBlock value)
        {
            List<BoundingBox> boundingBoxes = new List<BoundingBox>();
            DrawCache[] drawCaches = null;
            switch (DeviceType)
            {
                case DeviceType.Electric:
                    ElectricPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
                case DeviceType.Fluid:
                    FliudPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
                case DeviceType.Gas:
                    GasPoints.TryGetValue(value.Offset, out drawCaches);
                    break;
            }
            if (drawCaches == null) return Block.m_defaultCollisionBoxes;
            for (int i = 0; i < drawCaches.Length; i++)
            {
                if(drawCaches[i].isVisible)boundingBoxes.Add(drawCaches[i].item.BlockMesh.CalculateBoundingBox());
            }
            if (boundingBoxes.Count == 0)
            {
                boundingBoxes.Add(new BoundingBox(Vector3.Zero, Vector3.One));
            }
            return boundingBoxes.ToArray();
        }
        public override void OnBlockAdded(SubsystemBlockBehavior blockBehavior, int value, int oldValue, int x, int y, int z)
        {
            //自动识别面的模式
            Dictionary<int, MachineFaceConfig> configs = new Dictionary<int, MachineFaceConfig>();
            Point3 point = new Point3(x, y, z);
            for (int i = 0; i < 6; i++)
            {
                Point3 point1 = point + CellFace.FaceToPoint3(i);
                switch (DeviceType)
                {
                    case DeviceType.Item:
                        {
                            var element = ItemSystem.Get(point1);
                            if (element != null)
                            {
                                element.Instance.Machine.MachineFaceConfig.TryGetValue(CellFace.OppositeFace(i), out MachineFaceConfig conf);
                                if (!configs.ContainsKey(i)) configs.Add(i, new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.OUT : MachineMode.IN, DeviceType = DeviceType });
                                if (!configs.ContainsKey(CellFace.OppositeFace(i))) configs.Add(CellFace.OppositeFace(i), new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.IN : MachineMode.OUT, DeviceType = DeviceType });
                            }
                            break;
                        }
                    case DeviceType.Electric:
                        {
                            var element = ElectricSystem.Get(point1);
                            if (element != null)
                            {
                                element.Instance.Machine.MachineFaceConfig.TryGetValue(CellFace.OppositeFace(i), out MachineFaceConfig conf);
                                if (!configs.ContainsKey(i)) configs.Add(i, new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.OUT : MachineMode.IN, DeviceType = DeviceType });
                                if (!configs.ContainsKey(CellFace.OppositeFace(i))) configs.Add(CellFace.OppositeFace(i), new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.IN : MachineMode.OUT, DeviceType = DeviceType });
                            }
                            break;
                        }
                    case DeviceType.Fluid:
                        {
                            var element = FluidSystem.Get(point1);
                            if (element != null)
                            {
                                element.Instance.Machine.MachineFaceConfig.TryGetValue(CellFace.OppositeFace(i), out MachineFaceConfig conf);
                                if (!configs.ContainsKey(i)) configs.Add(i, new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.OUT : MachineMode.IN, DeviceType = DeviceType });
                                if (!configs.ContainsKey(CellFace.OppositeFace(i))) configs.Add(CellFace.OppositeFace(i), new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.IN : MachineMode.OUT, DeviceType = DeviceType });
                            }
                            break;
                        }
                    case DeviceType.Gas:
                        {
                            var element = GasSystem.Get(point1);
                            if (element != null)
                            {
                                element.Instance.Machine.MachineFaceConfig.TryGetValue(CellFace.OppositeFace(i), out MachineFaceConfig conf);
                                if (!configs.ContainsKey(i)) configs.Add(i, new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.OUT : MachineMode.IN, DeviceType = DeviceType });
                                if (!configs.ContainsKey(CellFace.OppositeFace(i))) configs.Add(CellFace.OppositeFace(i), new MachineFaceConfig() { MachineMode = conf.MachineMode == MachineMode.IN ? MachineMode.IN : MachineMode.OUT, DeviceType = DeviceType });
                            }
                            break;
                        }
                    default:

                        break;
                }

            }
            for (int i = 0; i < 6; i++)
            {
                if (!configs.ContainsKey(i)) configs.Add(i, new MachineFaceConfig() { MachineMode = MachineMode.IN, DeviceType = DeviceType });
            }
            int amount = GetOutInLimit(NameType);
            MachineAmountLimitConfig limitConfig = new MachineAmountLimitConfig();
            switch (DeviceType)
            {
                case DeviceType.Electric:
                    limitConfig.MaxPower = amount * 4;
                    limitConfig.InputPowerLimit = amount;
                    limitConfig.OutputPowerLimit = amount;
                    break;
                case DeviceType.Fluid:
                    limitConfig.InputFluidAmountLimit = amount;
                    limitConfig.OutputFluidAmountLimit = amount;
                    break;
                case DeviceType.Gas:
                    limitConfig.InputGasAmountLimit = amount;
                    limitConfig.OutputGasAmountLimit = amount;
                    break;
            }

            List<ComponentEnergyMachine.Slot> SlotsInit = new List<ComponentEnergyMachine.Slot>() { new ComponentEnergyMachine.Slot() { SlotType = ComponentEnergyMachine.SlotType.IN | ComponentEnergyMachine.SlotType.OUT, FuelCapacity = amount * 4 } };
            if (DeviceType == DeviceType.Electric || DeviceType == DeviceType.Item) SlotsInit = null;
            ILibrary.AddBaseEnergyEntity(DeviceType.ToString() + "Wire", x, y, z, SlotsInit, configs, limitConfig);
            switch (DeviceType)
            {
                case DeviceType.Item:
                    {
                        ItemSystem.Add(new ItemElement.ItemWireElement(x, y, z));
                        break;
                    }
                case DeviceType.Electric:
                    {
                        ElectricSystem.Add(new WireElement(x, y, z));
                        break;
                    }
                case DeviceType.Fluid:
                    {
                        FluidSystem.Add(new WireFluidElement(x, y, z));
                        break;
                    }
                case DeviceType.Gas:
                    {
                        GasSystem.Add(new WireGasElement(x, y, z));
                        break;
                    }
                default: break;

            }
        }
        public override void OnBlockGenerated(int value, int x, int y, int z, bool isLoaded)
        {
            if (isLoaded)
            {
                switch (DeviceType)
                {
                    case DeviceType.Item:
                        {
                            ItemSystem.Add(new ItemElement.ItemWireElement(x, y, z));
                            break;
                        }
                    case DeviceType.Electric:
                        {
                            ElectricSystem.Add(new WireElement(x, y, z));
                            break;
                        }
                    case DeviceType.Fluid:
                        {
                            FluidSystem.Add(new WireFluidElement(x, y, z));
                            break;
                        }
                    case DeviceType.Gas:
                        {
                            GasSystem.Add(new WireGasElement(x, y, z));
                            break;
                        }
                    default: break;

                }
            }
        }
        public override void OnBlockRemoved(SubsystemBlockBehavior blockBehavior, int value, int newValue, int x, int y, int z)
        {
            Point3 point = new Point3(x, y, z);
            switch (DeviceType)
            {
                case DeviceType.Item:
                    {
                        ItemSystem.Remove(x, y, z);
                        break;
                    }
                case DeviceType.Electric:
                    {
                        ElectricSystem.Remove(point);
                        break;
                    }
                case DeviceType.Fluid:
                    {
                        FluidSystem.Remove(point);
                        break;
                    }
                case DeviceType.Gas:
                    {
                        GasSystem.Remove(point);
                        break;
                    }
                default: break;

            }
            ILibrary.RemoveBlockEntity(x, y, z);
        }
        public override bool IsFaceTransparent(SubsystemTerrain subsystemTerrain, int face, int value)
        {
            return true;
        }
        public override DeviceType GetDeviceType()
        {
            return DeviceType;
        }
        public override string GetDisplayName(SubsystemTerrain subsystemTerrain, int value)
        {
            return DisplayName;
        }
    }
}
